﻿using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace DimensionsHelper.LanguageService.Syntax;

[DebuggerDisplay("{ToString(),nq}")]
public abstract class AbstractTextNode : AbstractTextRange, ITextNode
{
    private SyntaxComment? _leadingComment;
    private SyntaxList<SyntaxComment>? _leadingComments;
    private SyntaxNodeFlags _nodeFlags;
    private ITextNode? _parent;
    private SyntaxComment? _trailingComment;
    private SyntaxList<SyntaxComment>? _trailingComments;

    protected internal AbstractTextNode(SyntaxKinds kind, ITextNode? parent)
    {
        Kind = kind;
        _parent = parent;
    }

    public SyntaxKinds Kind { get; }

    public virtual ITextNode? Parent
    {
        get => _parent;
        internal set => _parent = value;
    }

    public bool Is<T>([MaybeNullWhen(false)] out T node) where T : ITextNode
    {
        node = default;

        if (this is not T obj)
        {
            return false;
        }

        node = obj;
        return true;
    }


    public bool IsNode(SyntaxKinds kind)
    {
        return Kind == kind;
    }

    public bool IsOneOfNode(params SyntaxKinds[] kinds)
    {
        return Array.IndexOf(kinds, Kind) != -1;
    }


    public virtual void AcceptVisitor(SyntaxNodeVisitor visitor)
    {
        AcceptVisitor<ITextNode>(visitor);
    }


    public virtual void AcceptVisitor<T>(SyntaxNodeVisitor<T> visitor) where T : class, ITextNode
    {
        visitor.VisitComment(this);
    }


    public T? FindParent<T>() where T : ITextNode
    {
        ITextNode? p = Parent;

        while (p != null)
        {
            if (p.Is<T>(out T? res))
            {
                return res;
            }

            p = p.Parent;
        }

        return default;
    }


    public ITextNode? FindParent(Predicate<ITextNode> predicate)
    {
        ITextNode? p = Parent;

        while (p != null)
        {
            if (predicate(p))
            {
                return p;
            }
            
            p = p.Parent;
        }
        
        return null;
    }


    public SyntaxComment? GetLeadingComment()
    {
        return _leadingComment;
    }


    public ImmutableArray<SyntaxComment> GetLeadingComments()
    {
        return _leadingComments?.ToImmutableArray() ?? ImmutableArray<SyntaxComment>.Empty;
    }


    public SyntaxComment? GetTrailingComment()
    {
        return _trailingComment;
    }


    public ImmutableArray<SyntaxComment> GetTrailingComments()
    {
        return _trailingComments?.ToImmutableArray() ?? ImmutableArray<SyntaxComment>.Empty;
    }
    

    public bool Is<T>(SyntaxKinds kind, [MaybeNullWhen(false)] out T node) where T : AbstractTextNode
    {
        if (IsNode(kind))
        {
            node = (T)this;
            return true;
        }

        node = null;
        return false;
    }


    internal void SetFlags(SyntaxNodeFlags flags)
    {
        _nodeFlags |= flags;
    }

    internal SyntaxNodeFlags GetFlags()
    {
        return _nodeFlags;
    }

    internal bool GetFlags(SyntaxNodeFlags flags)
    {
        return (_nodeFlags & flags) != 0;
    }


    /// <summary>
    ///     使用给定的起始位置初始化节点。
    /// </summary>
    internal void Update(int start)
    {
        Start = start;
    }


    /// <summary>
    ///     只更新节点的起始位置和宽度
    /// </summary>
    internal void Update(int start, int length)
    {
        Start = start;
        Width = length;
    }


    /// <summary>
    ///     使用指定的起始和结束位置更新节点的区间和文本
    /// </summary>
    internal void Update(int start, int length, string text)
    {
        Update(start, length);
        Text = text;
    }


    /// <summary>
    ///     使用给定的结束位置和字符串结束此节点
    /// </summary>
    public void Update(int length, string text)
    {
        Width = length;
        Text = text;
    }


    internal void Update(in TextSpan span)
    {
        Start = span.Start;
        Width = span.Width;
    }


    /// <summary>
    ///     只更新节点的文本内容
    /// </summary>
    internal void Update(string text)
    {
        Text = text;
    }


    internal void Update(ITextRange range, string text)
    {
        Update(range.Start, range.Width);
        UpdateLine(range.StartLine, range.EndLine);
        Text = text;
    }


    internal void Update(in TextSpan span, string text)
    {
        Update(span);
        Text = text;
    }


    /// <summary>
    ///     使用指定的节点位置更新节点的区间和文本
    /// </summary>
    internal void UpdateFrom(ITextNode node)
    {
        Update(node.Start, node.Width, node.Text);
        UpdateLine(node.StartLine, node.EndLine);
        SetUri(node.Uri);
        Parent = node.Parent;
    }


    internal void SetLeadingComment(SyntaxComment? comment)
    {
        if (comment == null)
        {
            return;
        }

        _leadingComment = comment;
    }


    internal void AddLeadingComments(Span<SyntaxComment> comments)
    {
        _leadingComments ??= new SyntaxList<SyntaxComment>();
        _leadingComments.AddRange(comments);
    }


    internal void SetTrailingComment(SyntaxComment? comment)
    {
        if (comment == null)
        {
            return;
        }

        _trailingComment = comment;
    }


    internal void AddTrailingComments(Span<SyntaxComment> comments)
    {
        _trailingComments ??= new SyntaxList<SyntaxComment>();
        _trailingComments.AddRange(comments);
    }


    public override string ToString()
    {
        return Text;
    }


    protected static void Accept<T>(SyntaxNodeVisitor<T> visitor, params ITextNode?[] nodes)
        where T : class, ITextNode
    {
        AcceptList(visitor, nodes);
    }


    protected static void AcceptList<T>(SyntaxNodeVisitor<T> visitor, IEnumerable<ITextNode?>? nodes)
        where T : class, ITextNode
    {
        if (visitor.IsFinished || nodes == null)
        {
            return;
        }

        foreach (ITextNode? node in nodes)
        {
            if (node is not T target)
            {
                continue;
            }

            if (node.IsEmpty || !visitor.Predicate(target))
            {
                continue;
            }

            node.AcceptVisitor(visitor);
            if (visitor.IsFinished)
            {
                return;
            }
        }
    }


    protected void AddChildMember<T>(ref SyntaxList<T>? field, T value)
        where T : AbstractTextNode
    {
        field ??= new SyntaxList<T>();
        AddChildMemberToReadOnlyField(field, value);
    }


    protected void AddChildMemberToReadOnlyField<T>(SyntaxList<T> field, T value)
        where T : AbstractTextNode
    {
        if (value.IsEmpty)
        {
            return;
        }

        field.Add(value);
        value.Parent = this;
    }


    protected void SetChildField<T>([NotNull] ref T? field, T value)
        where T : AbstractTextNode
    {
        if (field != null && ReferenceEquals(field, value))
        {
            return;
        }

        if (field != null)
        {
            field.Parent = null;
        }

        field = value;
        field.Parent = this;
    }


    protected void SetChildField<T>(ref T field, Func<T, bool> emptyChecker, T value)
        where T : AbstractTextNode
    {
        if (field == value)
        {
            return;
        }

        if (!emptyChecker(field))
        {
            field.Parent = null;
        }

        if (emptyChecker(value))
        {
            return;
        }

        field = value;
        value.Parent = this;
    }


    protected void SetChildNullableField<T>(ref T? field, T? value)
        where T : AbstractTextNode
    {
        SetChildNullableField(ref field, null, value);
    }


    protected void SetChildNullableField<T>(ref T? field, Func<T, bool>? emptyChecker, T? value)
        where T : AbstractTextNode
    {
        if (field == value)
        {
            return;
        }

        if (field != null && (emptyChecker == null || !emptyChecker(field)))
        {
            field.Parent = null;
        }

        field = value;

        if (value != null && (emptyChecker == null || !emptyChecker(value)))
        {
            value.Parent = this;
        }
    }


    /// <summary>
    ///     获取语法节点上的所有错误
    /// </summary>
    /// <returns></returns>
    public virtual IEnumerable<DiagnosticInfo> GetDiagnostics()
    {
        return this.GetSyntaxTree()?.GetDiagnostics(this) ?? [];
    }


    /// <summary>
    ///     如果当前节点带有指定错误描述的错误，返回<see langword="true"/>。
    /// </summary>
    /// <param name="descriptor"></param>
    /// <returns></returns>
    public bool ContainsDiagnostic(DiagnosticDescriptor descriptor)
    {
        return GetDiagnostics().Any(d => d.Descriptor == descriptor);
    }


    public AbstractTextNode Copy()
    {
        return (AbstractTextNode)MemberwiseClone();
    }
}